﻿using System;
using System.IO;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;

namespace LifeInSharePoint.Common.Base
{
    /// <summary> 
    /// Base class for web part development. 
    /// All web parts should inherit from this class. 
    /// Exceptions thrown from web parts inheriting from this base 
    /// will not crash the Page the web part is on, but rather do one of two things:
    /// 
    /// 1)If compiled in debug mode: Render the stacktrace of the exception inline in the web page
    /// 2)If compiled in release mode: Render a friendly error message inline in the web page.
    /// 
    /// This behaviour can be overridden by inheritors by overriding the method HandleException
    ///  
    /// HOW THIS WORKS:
    /// -------
    /// In order to wrap each framework call with exception handling, 
    /// we have a two-tiered approach in which 
    /// 1) the first tier (ExceptionHandlingWebPartBase) overrides and seals methods, 
    /// 2) then applies try/catch to a new set of methods, forwarding method parameters
    /// 3) these new methods are overridden in the second tier (BaseWebPart)
    /// 4) where they are sealed and a call is made to new virtual methods that are named the 
    /// same as the framework methods.
    /// 5) These methods (now with a catch-block around them) are then overridden as needed in a 
    /// regular web part that inherits from BaseWebPart. The exception handling is thus 
    /// transparent to the inheritor.
    /// </summary>
    public abstract class BaseWebPart : ExceptionHandlingWebPartBase
    {
        #region temp methods for method piping (overrides and seals methods from ExceptionHandlingWebPartBase)
        /*
         * These methods re part of the plumbing necessary to give inheritors
         * the expected interface.
         */
        public override sealed void RenderWebPart(HtmlTextWriter writer)
        {
            Render(writer);
        }

        public override sealed void CreateWebPartChildControls()
        {
            CreateChildControls();
        }

        public override sealed void InitWebPart(EventArgs e)
        {
            OnInit(e);
        }

        public sealed override void PreRenderWebPart(EventArgs e)
        {
            OnPreRender(e);
        }

        public sealed override void LoadWebPart(EventArgs e)
        {
            OnLoad(e);
        }
        #endregion
        #region Methods in which exceptions are now handled.
        protected new virtual void Render(HtmlTextWriter writer)
        {
            base.RenderWebPart(writer);
        }
        protected new virtual void CreateChildControls()
        {
            base.CreateWebPartChildControls();
        }

        protected new virtual void OnInit(EventArgs e)
        {
            base.InitWebPart(e);
        }

        protected new virtual void OnLoad(EventArgs e)
        {
            base.LoadWebPart(e);
        }

        protected new virtual void OnPreRender(EventArgs e)
        {
            base.PreRenderWebPart(e);
        }
        #endregion
    }


    public abstract class ExceptionHandlingWebPartBase : WebPart
    {

        #region Exception handling section
        private StringBuilder _errorOutput;
        private bool _abortProcessing;
        public virtual bool AbortProcessing
        {
            get { return _abortProcessing; }
            set { _abortProcessing = value; }
        }

        public virtual void HandleException(Exception e, HtmlTextWriter writer)
        {
            #if !DEBUG
                     writer.Write("An Error has Occured in this WebPart.  Please Check your settings and try again");
            #else
                     writer.Write(e.Message + "<br/>" + e.StackTrace);
            #endif

        }

        public void ExceptionHappened(Exception ex)
        {
            AbortProcessing = true;
            //TODO: use own logging framework here:
            //Logger.Log(Severity.Error, ex.Message + " " + ex.StackTrace);

            HandleException(ex, new HtmlTextWriter(new StringWriter(_errorOutput)));
        }

        #endregion

        #region Override framework methods for method piping
        protected override sealed void CreateChildControls()
        {

            if (!AbortProcessing)
            {
                try
                {
                    CreateWebPartChildControls();
                }
                catch (Exception e)
                {
                    ExceptionHappened(e);
                }
            }
        }

        protected override sealed void OnInit(EventArgs e)
        {
            AbortProcessing = false;

            _errorOutput = new StringBuilder();

            try
            {
                InitWebPart(e);
            }
            catch (Exception ex)
            {
                ExceptionHappened(ex);
            }
        }

        protected override sealed void Render(HtmlTextWriter writer)
        {
            StringBuilder tempOutput = new StringBuilder();
            if (!AbortProcessing)
            {
                HtmlTextWriter tempWriter = new HtmlTextWriter(new StringWriter(tempOutput));

                try
                {
                    RenderWebPart(tempWriter);
                }
                catch (Exception ex)
                {
                    ExceptionHappened(ex);
                }
            }
            if (AbortProcessing)
            {
                writer.Write(_errorOutput.ToString());
            }
            else
            {
                writer.Write(tempOutput.ToString());
            }
        }
        protected override sealed void OnLoad(EventArgs e)
        {
            if (!AbortProcessing)
            {
                try
                {
                    LoadWebPart(e);
                }
                catch (Exception ex)
                {
                    ExceptionHappened(ex);
                }


            }
        }
        protected override sealed void OnPreRender(EventArgs e)
        {
            if (!AbortProcessing)
            {

                try
                {
                    PreRenderWebPart(e);
                }
                catch (Exception ex)
                {
                    ExceptionHappened(ex);
                }

            }
        }
        #endregion

        #region Temp methods for method piping (will be overridden and sealed in subclass)
        public virtual void RenderWebPart(HtmlTextWriter writer)
        {
            EnsureChildControls();
            base.Render(writer);
        }
        public virtual void CreateWebPartChildControls()
        {
            base.CreateChildControls();
        }
        public virtual void InitWebPart(EventArgs e)
        {
            base.OnInit(e);
        }
        public virtual void LoadWebPart(EventArgs e)
        {
            base.OnLoad(e);
        }
        public virtual void PreRenderWebPart(EventArgs e)
        {
            base.OnPreRender(e);
        }
        #endregion
    }
}
